runnに逐次的に処理を進めるattachモードが追加されたので試してみた

runnに逐次的に処理を進めるattachモードが追加されたので試してみた

yamlで書いたシナリオをそのまま実行できるシナリオテストツールrunnにブレークポイントを貼ったりステップ実行ができるattachモードが追加されたので試してみました
Clock Icon2024.02.29

@katzchum runnに --attach オプション追加しましたhttps://t.co/h3G3PiXPys
いわゆるデバッグオプションです。
コマンド体系は gdb を参考にしています。
--verboseと合わせて使ってみてくださいー#runn開発者会議

— k1LoW (@k1LoW) February 28, 2024

というわけで早速試してみましょう!

実行サンプル

前回の yamlでテストシナリオを書いてそのまま実行までできるAPIテストツールの新星 “runn” を試してみた のサンプルをそのまま使います。

desc: アクセストークンを発行し、テナントを登録する
runners:
  req: http://localhost:8080
steps:
  createToken:
    desc: アクセストークンを発行する
    req:
      /oauth/token:
        post:
          body:
            application/json:
              # リクエストパラメータをKey-Value形式で指定する
              subject: "example"
              issuer: "http://localhost"
              expires_in: 86400
    test: |
      # 処理が成功し、JWT形式のアクセストークンが発行されたか検証する
      current.res.status == 200
      && (current.res.body.access_token startsWith "eyJ") == true
    # 以後のリクエストで使えるよう、発行されたトークンを束縛しておく
    bind:
      access_token: current.res.body.access_token

bindTenantCode:
    bind:
      # UUIDを生成してテナントコードとして使用する
      tenant_code: faker.UUID()

createTenant:
    desc: テナントを登録する
    req:
      /tenants:
        post:
          headers:
            # 発行されたアクセストークンを参照して指定する
            authorization: "Bearer {{ access_token }}"
          body:
            application/json:
              # bindしたテナントコードを利用する
              tenant_code: "{{ tenant_code }}"
              name: "テスト用テナント"
              title: "テスト用テナントタイトル"
    test: |
      # 処理が成功し、指定したのと同じテナントコードでリソースが作成されたか検証する
      current.res.status == 200
      && current.res.body.tenant_code == tenant_code

使い方

リリースノート

Release v0.99.4 · k1LoW/runn

を見てみると、--attach オプションが追加されたGitHub Pull Requestのリンクがあるので見てみましょう。

Add --attach option for debugging or step execution by k1LoW · Pull Request #817 · k1LoW/runn

With this fix, runn gets a GDB-like debugger.

ふむふむ、GDBというのを参考にすればよいのですね。

GDB?

runnはGo言語で作られているので、そちら方面のツールだろうということで検索したら、早速出てきました。

Debugging Go Code with GDB - The Go Programming Language

このページを参考にしつつ、触ってみましょう。

やってみた

まずは雑に--attachオプションを付けて実行してみましょう。

$ runn run scenario.yaml --attach

すると、REPLのような入力待ちモードになりました。

list:現在のステップをリスト表示する

先述のGDBのドキュメントを見て、まずはlistを実行してみます。list の先頭文字の l を入力すると、候補が表示されます。

確定するには Tab キーを押します。

そのまま Enter キーを押します。

最初のステップで処理を止めていることがわかり、そのステップの内容が表示されました。

next:ステップを次に進める

現在のステップを実行し、ステップを次に進めるには next もしくは短縮形の n コマンドを使います。

そのままEnter を押します。

すると、createTokenステップが実行され、次のbindTenantCodeステップで処理が止まります。

print:変数の値を見る

この時点で変数の値を確認してみましょう。それには print もしくは短縮形の p コマンドを使います。

そのままSpaceキーを押すと、print可能な変数の候補が表示されます。

ここでは、createTokenステップで束縛したaccess_tokenを見てみましょう。a を入力すると、候補が絞り込まれます。

Tabキーを押して確定した後、Enterキーを押しましょう。

createTokenステップで取得したアクセストークンの値が表示されます。

printコマンドはステップの結果を見ることもできます。createTokenステップを指定してみましょう。

当然のように previous も見れます。

quit:終了する

デバッグモードを終了するにはquitもしくは短縮形のqコマンドを使います。

実行すると、シナリオが最後まで実行されず、スキップされた事がわかります。

break:ブレークポイントを貼る

デバッグするとき、ある程度処理を飛ばして特定のところで止めたい事がよくあります。そんなときは、ブレークポイントを使いましょう。止めたいところにブレークポイントを貼るには、break もしくは短縮形のbコマンドを使います。

こちらも他のコマンドと同様に、候補となるステップ名がサジェストされます。今回はcreateTenantステップを指定しましょう。ステップ名を指定するには、ステップ名の先頭に : を付けないといけないようです。

continue:処理を継続して進める

ブレークポイントを貼ったところまで処理を継続して進めるには、continueまたは短縮形のcコマンドを使います。

そのままEnterを押すと、処理が継続され、先程ブレークポイントを貼ったcreateTenantステップまで処理が進みます。

ここでprintコマンドを使うと、createTokenコマンドとcreateTenantステップのあいだにあったbindTenantCodeステップで束縛したtenant_codeが指定できるようになったことがわかります。

また、steps変数をprintしてみると、これまで実行されたステップの結果が記録されているコもわかります。

info:情報を表示する

info もしくは短縮形の i コマンドを使うと、実行しているシナリオに関する情報が表示できます。

2024/2/29リリースのバージョン0.99.4では、作成したブレークポイントの情報表示だけ対応しているように見えます。

bを入力してEnterを押してみると、ブレークポイントの一覧が表示されました。

まとめ

プログラマブルなシナリオでは、「思った通りに動いていないみたいだけど何故だー!?」ってことが稀によくあります。もともとrunnには --debug オプションで詳細を表示しながら実行するモードはありますが、今回追加された --attach モードと組み合わせることで、さらに原因調査が捗りそうですね!

余談:infoコマンドの対応内容

やはり現時点ではブレークポイントのみの対応で間違いなさそうです。

https://github.com/k1LoW/runn/blob/24775d1779d207a00f1ddc13d78e7162fbaed6dd/dbg.go#L218-L243

        case dbgCmdInfo, dbgCmdInfoShort:
            // info
            if len(cmd) != 2 {
                _, _ = fmt.Fprintf(os.Stderr, "args required")
                continue
            }
            switch cmd[1] {
            case "breakpoints", "b":
                table := tablewriter.NewWriter(os.Stdout)
                table.SetHeader([]string{"Num", "ID", "Step"})
                table.SetAutoWrapText(false)
                table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
                table.SetAutoFormatHeaders(false)
                table.SetCenterSeparator("")
                table.SetColumnSeparator("")
                table.SetRowSeparator("-")
                table.SetHeaderLine(false)
                table.SetBorder(false)
                for i, bp := range d.breakpoints {
                    table.Append([]string{strconv.Itoa(i + 1), bp.runbookID, bp.stepKey})
                }
                table.Render()
            default:
                _, _ = fmt.Fprintf(os.Stderr, "unknown args %s\n", cmd[1])
                continue
            }

今後の発展が期待できますね!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.